package com.example.demo.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import com.example.demo.common.AjaxResult;
import com.example.demo.common.AppVariable;
import com.example.demo.common.PasswordUtils;
import com.example.demo.common.UserSessionUtils;
import com.example.demo.entity.UserQue;
import com.example.demo.entity.Userinfo;
import com.example.demo.entity.vo.UserinfoVO;
import com.example.demo.service.ArticleService;
import com.example.demo.service.DiscussService;
import com.example.demo.service.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;

@RestController
@CrossOrigin
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private ArticleService articleService;
    @Autowired
    private DiscussService discussService;

    /**
     *  注册功能. 即数据库的添加操作.
     */
    @RequestMapping("/reg") //
    public AjaxResult register(Userinfo userinfo) {
        // 非空校验 和 参数有效性校验
        if (userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) ||
                !StringUtils.hasLength(userinfo.getPassword())) {
            // 以统一对象 AjaxResult 格式进行返回.
            return AjaxResult.fail(-1, "非法参数");
        }
        // 查看用户名是否已经存在
        if (userService.getUserByName(userinfo.getUsername()) != null) {
            return AjaxResult.fail(-2, "非法请求!");
        }

        //  用户名必须包含数字,字母,特殊字符任意两种
        String userStr = "^(?![a-zA-z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[a-zA-Z\\d!@#$%^&*]+$";
         if (!userinfo.getUsername().matches(userStr)) {
             return AjaxResult.fail(-3, "用户名必须包含数字,字母,特殊字符任意两种");
         }

        // 密码必须是 数字, 字符, 字母的组合 并且 长度在6-18位
        String str = "^(?![0-9]+$)(?![a-zA-Z]+$)(?![0-9a-zA-Z]+$)(?![0-9\\W]+$)(?![a-zA-Z\\W]+$)[0-9A-Za-z\\W]{6,18}$";
        if (!userinfo.getPassword().matches(str)) {
            return AjaxResult.fail(-3, "密码必须包含字母、数字、特殊字符组成, 且在6 - 18位之间.");
        }

        // 生成随机昵称
        UserinfoVO userinfoVO = new UserinfoVO();
        String nickNAme = "用户" + UUID.randomUUID().toString().replace("-","").substring(0,6);
        userinfoVO.setNickname(nickNAme);
        // 密码加盐处理
        userinfo.setPassword(PasswordUtils.encrypt(userinfo.getPassword()));
        System.out.println(userinfo.getPassword());
        // 深拷贝
        BeanUtils.copyProperties(userinfo, userinfoVO);
        // 以统一对象 AjaxResult 格式进行返回.
        return AjaxResult.success(userService.register(userinfoVO));
    }


    /**
     *  登录之前添加验证码.
     */
    @GetMapping("/getcode")
    public void getCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 通知浏览器不要缓存
        response.setHeader("pragma", "no-cache");
        response.setHeader("cache-control", "no-cache");
        response.setHeader("expires", "0");

        // 生成一个验证码图片                                                // 宽          高   几位数的验证码     干扰点
        CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(116, 36, 4, 10);

        // 拿到图片生成的验证码
        String code = circleCaptcha.getCode();
        System.out.println("code: " + code);

        // 每调用一次此方法, 就会生成新的 code, 存到 session[redis] 中
        HttpSession session = request.getSession();
        session.setAttribute(AppVariable.LOGIN_CHECK_CODE, code);

        // 拿到流对象
        ServletOutputStream outputStream = response.getOutputStream();
        if (outputStream != null) {
            // 通过输出流输出给前端
            circleCaptcha.write(outputStream);
            // 关闭资源
            outputStream.close();
        }
    }


    /**
     *  登录功能, 多次登录会被冻结.
     */
    @RequestMapping("/login")
    public AjaxResult login(String username, String password, String code, HttpServletRequest request) {
        // 1. 非空校验
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)
                        || !StringUtils.hasLength(code)) {
            return AjaxResult.fail(-1, "非法请求");
        }
        //  用户名必须包含数字,字母,特殊字符任意两种
        String userStr = "^(?![a-zA-z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[a-zA-Z\\d!@#$%^&*]+$";
        if (!username.matches(userStr)) {
            return AjaxResult.fail(-3, "用户名必须包含数字,字母,特殊字符任意两种");
        }

        // 密码必须是 数字, 字符, 字母的组合 并且 长度在6-18位
        String str = "^(?![0-9]+$)(?![a-zA-Z]+$)(?![0-9a-zA-Z]+$)(?![0-9\\W]+$)(?![a-zA-Z\\W]+$)[0-9A-Za-z\\W]{6,18}$";
        if (!password.matches(str)) {
            return AjaxResult.fail(-3, "密码必须包含字母、数字、特殊字符组成, 且在6 - 18位之间.");
        }

        // 拿到验证码 session
        String imgCode = UserSessionUtils.getImgCode(request);
        System.out.println(imgCode);
        // 自动化测试: 验证码一直都是1111
        if (!code.equals(imgCode) && code.equals("1111")) {
            // 验证码错误
            return AjaxResult.fail(-6,"参数验证错误");
        }
        // 验证 验证码是否正确
        // 2. 查询数据库
        Userinfo userinfo = userService.getUserByName(username);
        if (userinfo == null) {
            // 账号未注册
            return AjaxResult.fail(-7, "非法请求!");
        }
        if (userinfo.getId() > 0) {
            if ( userinfo.getState() != 1) {
                // 账号被冻结
                return AjaxResult.fail(-2, "非法用户");
            }
            // 是有效的用户名, 接着判断密码是否正确
            if (PasswordUtils.check(password, userinfo.getPassword())) {
                // 密码正确 登录成功.
                // 拿到 session 对象.
                HttpSession session = request.getSession();
                // 将用户存储到 session 中, AppVariable.USER_SESSION_KEY 是一个全局变量
                session.setAttribute(AppVariable.USER_SESSION_KEY, userinfo);
                // 返回数据之前, 隐藏用户敏感信息(密码)
                userinfo.setPassword("");
                // 恢复 logcount 字段的默认值.
                userinfo.setLogcount(AppVariable.USER_LOGIN_COUNT);
                userService.reduceLogCount(userinfo.getLogcount(),userinfo.getId());

                // 深拷贝
                UserinfoVO userinfoVO = new UserinfoVO();
                BeanUtils.copyProperties(userinfo, userinfoVO);
                return AjaxResult.success(userinfoVO);
            } else {
                // 用户名有效 但是密码不正确. logcount 要 -1
                userinfo.setLogcount(userinfo.getLogcount() - 1);
                userService.reduceLogCount(userinfo.getLogcount(),userinfo.getId());
                if (userinfo.getLogcount() <= 0) {
                    // 冻结账号
                    userinfo.setState(0);
                    userService.setUserState(userinfo.getState(), userinfo.getId());
                }
                return AjaxResult.fail(-1, userinfo.getLogcount() + "", null);
            }
        }
        // 走到这里 登陆失败(前端代码 data != null 才登录成功).
        return AjaxResult.success(0, null);
    }

    /**
     *  查询列表页的用户发表的信息
     */
    @RequestMapping("/showinfo")
    public AjaxResult showInfo(HttpServletRequest request) {
        UserinfoVO userinfoVO = new UserinfoVO();
        // todo: 1. 从 session中得到当前登录用户.
        // [该功能会用很多次, 设计为公共方法, 放到 common 包中]
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if (userinfo == null) {
            return AjaxResult.fail(-1, "非法请求");
        }
        // Spring 提供深拷贝的方式: 将 userinfo 中相同的字段拷贝到 userinfoVO.
        BeanUtils.copyProperties(userinfo, userinfoVO);
        // todo: 2. 得到用户发表的文章总数.
        userinfoVO.setArtCount(articleService.getArtCountBYUid(userinfo.getId()));
        // todo: 查询草稿数量.
        userinfoVO.setDraftCount(articleService.getDraftCount(userinfo.getId(), -1));
        // todo: 获取用户收藏的文章数目
        userinfoVO.setCollectCount(articleService.selUserUpvoteCount(userinfoVO.getId()));
        // 设置昵称
        userinfoVO.setNickname(userService.getNickName(userinfo.getId()));
        // userinfoVO.setPassword("");
        return AjaxResult.success(userinfoVO);
    }


    /**
     *  注销(退出登录)功能: 将用户从session中清除即可.
     */
    @RequestMapping("/logout")
    public AjaxResult logout(HttpSession session) {
        // 将用户从session中清楚即可
        session.removeAttribute(AppVariable.USER_SESSION_KEY);
        return AjaxResult.success(1);
    }

    /**
     *  7. 根据用户id查询列表页用户信息
     */
    @RequestMapping("/showuser")
    public AjaxResult getUserById(Integer id) {
        if (id == null || id <= 0) {
            return AjaxResult.fail(-1, "非法参数");
        }
        UserinfoVO userinfoVO = new UserinfoVO();
        Userinfo userinfo = userService.getUserById(id);

        if (userinfo == null || userinfo.getId() <= 0) {
            return AjaxResult.fail(-1, "非法参数");
        }
        // 隐藏敏感数据(密码)
        userinfo.setPassword("");
        // 深拷贝
        BeanUtils.copyProperties(userinfo, userinfoVO);
        // 获取文章草稿数目
        userinfoVO.setDraftCount(articleService.getDraftCount(userinfo.getId(), -1));
        // 获取文章数
        userinfoVO.setArtCount(articleService.getArtCountBYUid(userinfo.getId()));
        // 获取用户昵称
        userinfoVO.setNickname(userService.getNickName(userinfo.getId()));

        return AjaxResult.success(userinfoVO);
    }

    /**
     *  修改密码
     */
    @RequestMapping("/updpassword")
    public AjaxResult updPassword(String oldPassword, String password, HttpServletRequest request) {
        // 拿到当前登录对象
        if (oldPassword == null || password == null) {
            return AjaxResult.fail(-1, "非法参数");
        }
        // 验证旧密码是否正确
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if (userinfo == null) {
            return AjaxResult.fail(-1, "非法用户");
        }
        // 从数据库查询用户密码
        userinfo = userService.getUserByName(userinfo.getUsername());
        if (!PasswordUtils.check(oldPassword, userinfo.getPassword())) {
            // 旧密码不正确
            return AjaxResult.fail(-3, "参数错误!");
        }
        if (oldPassword.equals(password)) {
            // 旧密码和新密码相同
            return AjaxResult.fail(-4, "参数错误!");
        }
        // 密码加盐
        userinfo.setPassword(PasswordUtils.encrypt(password));
        // 让对象重新登陆.
        HttpSession session = request.getSession();
        session.removeAttribute(AppVariable.USER_SESSION_KEY);
        return AjaxResult.success(userService.updPassword(userinfo));
    }

    /**
     *  设置密保问题
     */
    @RequestMapping("/secret")
    public AjaxResult addQuestion(UserQue userQue, HttpServletRequest request) {
        if (userQue == null || !StringUtils.hasLength(userQue.getQuestion())
                || !StringUtils.hasLength(userQue.getAnswer())) {
            return AjaxResult.fail(-2, "非法请求!");
        }
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if (userinfo == null) {
            return AjaxResult.fail(-2, "非法用户!");
        }
        // 设置密保归属
        userQue.setUid(userinfo.getId());
        // 密保加盐处理.
        userQue.setAnswer(PasswordUtils.encrypt(userQue.getAnswer()));
        return AjaxResult.success(userService.addQuestion(userQue));
    }

    /**
     *  通过用户名 拿到用户 id
     */
    @RequestMapping("/checkuser")
    public AjaxResult getUserQuestion(String username) {

        if (!StringUtils.hasLength(username)) {
            return AjaxResult.fail(-1, "非法请求!");
        }
        // 通过用户名得到用户 id
        Userinfo userinfo = userService.getUserByName(username);
        if (userinfo == null) {
            return AjaxResult.fail(-1, "非法用户!");
        }
        return AjaxResult.success(userinfo.getId());
    }

    /**
     *  修改用户昵称
     */
    @RequestMapping("/updnickname")
    public AjaxResult updNickName(UserinfoVO userinfoVO, HttpServletRequest request) {
        if (userinfoVO == null || !StringUtils.hasLength(userinfoVO.getNickname())) {
            return AjaxResult.fail(-1, "非法参数");
        }
        // 从 session 中获取用户对象
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if (userinfo == null) {
            return AjaxResult.fail(-1, "非法请求");
        }
        // 深拷贝
        BeanUtils.copyProperties(userinfo, userinfoVO);
        // 将评论表的用户昵称进行同步修改
        discussService.setUserNickName(userinfoVO.getNickname(), userinfoVO.getId());
        // 进行修改
        return AjaxResult.success(userService.updNickName(userinfoVO));
    }

    /**
     *  上传头像功能
     */
    @RequestMapping("/photo")
    public AjaxResult upLoadPhoto(@RequestPart("file") MultipartFile multipartFile,
                                  HttpServletRequest request) throws IOException {

        // todo: 判断文件大小

        // 拿到登录用户
        Userinfo userinfo = UserSessionUtils.getUser(request);
        if (userinfo == null) {
            return AjaxResult.fail(-1, "非法操作");
        }
        // 保存的根目录.
       StringBuilder filePath = new StringBuilder(AppVariable.UPLOADS_PATH); // D:\image
       //  StringBuilder filePath = new StringBuilder("D:\\image\\"); //
        // 根目录 + 唯一文件名
        String fileName = UUID.randomUUID().toString().replace("-", "");
        filePath.append(fileName);
        // 根目录 + 唯一文件名 + 后缀名
        String fileLastName = Objects.requireNonNull(multipartFile.getOriginalFilename()).substring(
                multipartFile.getOriginalFilename().lastIndexOf("."));
        filePath.append(fileLastName);
        System.out.println("文件路径: " + filePath.toString());
        multipartFile.transferTo(new File(filePath.toString()));
        // 保存好文件名后 在数据库中将 photo 字段文件名.
        String photo = "/photo/" + fileName + fileLastName;
        userinfo.setPhoto(photo);
        // 将评论表的头像进行修改
        discussService.setUserPhoto(userinfo.getId(), userinfo.getPhoto());
        int result = userService.upLodePhoto(photo, userinfo.getId());

        // 刷新 session
        HttpSession session = request.getSession();
        session.removeAttribute(AppVariable.USER_SESSION_KEY);
        session.setAttribute(AppVariable.USER_SESSION_KEY, userinfo);

        // System.out.println(photo);
        return AjaxResult.success(result);
    }

}
